/**
 * @Copyright：<a href="www.wisesoft.net.cn">fxsen</a>
 * @Author: <a href="mailto:fxsen@foxmail.com">Fxsen</a>
 * @CreateTime: 2022年08月03日  15:10
 */
package com.fxsen.commons.web.core.aspect;

import com.fxsen.commons.core.base.common.OnlineUser;
import com.fxsen.commons.core.base.common.entity.JwtUser;
import com.fxsen.commons.core.base.result.ResponseResult;
import com.fxsen.commons.sysmanager.core.entity.OperationLog;
import com.fxsen.commons.sysmanager.core.service.IOperationLogService;
import com.fxsen.commons.utils.HttpContextUtils;
import com.fxsen.commons.utils.JwtUtils;
import com.fxsen.commons.utils.ThrowableUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 *  日志AOP
 * @date 2022/3/31 9:03
 * @author fxsen <br/>
 * @email <a href="mailto:fxsen@foxmail.com">
 **/
@Aspect
@Component
public class LogAopAspect {

    private static final Logger logger = LoggerFactory.getLogger(LogAopAspect.class);

    ThreadLocal<Long> currentTime = new ThreadLocal<>();

    @Autowired
    private IOperationLogService operationLogService;

    /**
     * 配置切入点
     */
    @Pointcut("@annotation(com.fxsen.commons.core.annotation.RecordLog)")
    public void logPointcut(){
        // 该方法无方法体,主要为了让同类中其他方法使用此切入点
    }

    /**
     * 配置环绕通知,使用在方法logPointcut()上注册的切入点
     *
     * @param joinPoint join point for advice
     */
    @Around("logPointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result;
        currentTime.set(System.currentTimeMillis());
        result = joinPoint.proceed();
        OperationLog log = new OperationLog();
        log.setType(1);
        log.setTime(System.currentTimeMillis() - currentTime.get());
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        log.setRequestMethod(request.getMethod());
        log.setRequestUrl(request.getRequestURL().toString());
        currentTime.remove();
        operationLogService.save(getUsername(request),HttpContextUtils.getBrowser(request),HttpContextUtils.getIpAddress(request),joinPoint,log);
        return result;
    }
    /**
     * 配置异常通知
     *
     * @param joinPoint join point for advice
     * @param e exception
     */
    @AfterThrowing(pointcut = "logPointcut()", throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
        currentTime.set(System.currentTimeMillis());
        OperationLog log = new OperationLog();
        log.setType(2);
        log.setTime(System.currentTimeMillis() - currentTime.get());
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        log.setRequestMethod(request.getMethod());
        log.setRequestUrl(request.getRequestURL().toString());
        log.setExMessage(ThrowableUtils.getStackTrace(e));
        currentTime.remove();
        operationLogService.save(getUsername(request),HttpContextUtils.getBrowser(request),HttpContextUtils.getIpAddress(request),(ProceedingJoinPoint)joinPoint,log);
    }
    /**
     * 前置通知，方法调用前被调用
     * @param joinPoint
     */
    @Before("logPointcut()")
    public void doBefore(JoinPoint joinPoint){
        System.out.println("我是前置通知!!!");
        //获取目标方法的参数信息
        Object[] obj = joinPoint.getArgs();
        Signature signature = joinPoint.getSignature();
        //代理的是哪一个方法
        System.out.println("方法："+signature.getName());
        //AOP代理类的名字
        System.out.println("方法所在包:"+signature.getDeclaringTypeName());
        //AOP代理类的类（class）信息
        signature.getDeclaringType();
        MethodSignature methodSignature = (MethodSignature) signature;
        String[] strings = methodSignature.getParameterNames();
        System.out.println("参数名："+ Arrays.toString(strings));
        System.out.println("参数值ARGS : " + Arrays.toString(joinPoint.getArgs()));
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest req = attributes.getRequest();
        // 记录下请求内容
        System.out.println("请求URL : " + req.getRequestURL().toString());
        System.out.println("HTTP_METHOD : " + req.getMethod());
        System.out.println("IP : " + req.getRemoteAddr());
        System.out.println("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());

    }

    /**
     * 处理完请求返回内容
     * @param ret
     * @throws Throwable
     */
    @AfterReturning(returning = "ret", pointcut = "logPointcut()")
    public void doAfterReturning(Object ret) throws Exception {
        System.out.println("处理完请求返回内容.....");
        // 处理完请求，返回内容
        if(ret instanceof ResponseResult) {
            System.out.println(((ResponseResult)ret).getCode());
        }
    }

    /**
     * 后置异常通知
     * @param jp
     */
    @AfterThrowing("logPointcut()")
    public void throwss(JoinPoint jp){
        System.out.println("后置异常通知，方法异常时执行.....");
    }

    /**
     * 后置最终通知,final增强，不管是抛出异常或者正常退出都会执行
     * @param pjp
     *//*
    @After("logPointcut()")
    public void recordLog(JoinPoint pjp) throws Throwable{
        System.out.println("后置最终通知,final增强，不管是抛出异常或者正常退出都会执行.....");
        // 这里记录日志
        MethodSignature signature = (MethodSignature) (pjp.getSignature());
        Class<?> currentClass = pjp.getTarget().getClass();
        String className = currentClass.getSimpleName();
        String methodName = currentClass.getMethod(signature.getName(), signature.getParameterTypes()).getName();
        logger.info("======= 开始执行：" + className + " — " + methodName + " ========");
        // 接收到请求，记录请求内容
        HttpServletRequest req = HttpContextUtils.getHttpServletRequest();
        String ipAddress = HttpContextUtils.getIpAddress(req);
        //获取到请求参数
        Object[] pjpArgs = pjp.getArgs();
        //获取到方法上RecordLog注解
        Method method = currentClass.getMethod(signature.getName(), signature.getParameterTypes());
        RecordLog recordLog = method.getAnnotation(RecordLog.class);
        String value = recordLog.value();
        OperationLog operationLog = new OperationLog();
        operationLog.setOperationUser(getUsername(req));
        operationLog.setType("登录".equals(value) || "登出".equals(value) ? 0 : 1);
        operationLog.setName(recordLog.value());
        operationLog.setRequestUrl(req.getRequestURL().toString());
        operationLog.setRequestMethod(req.getMethod());
        operationLog.setMethodName(signature.getDeclaringTypeName() + "." + signature.getName());
        operationLog.setParam(JSON.toJSONString(pjpArgs[0]));
        operationLog.setRequestIp(ipAddress);
        operationLog.setRealAddress(HttpContextUtils.getIpAddr(ipAddress));
        operationLogService.save(operationLog);
        logger.info("======= 执行结束：" + className + " — " + methodName + " ========");
    }*/
    private String getUsername(HttpServletRequest req){
        // 从请求头中取出 token  这里需要和前端约定好把jwt放到请求头一个叫Authorization的地方
        String token = req.getHeader(JwtUtils.JWT_HEADER_KEY);
        if (StringUtils.isNotBlank(token)){
            OnlineUser onlineUser = (OnlineUser)req.getSession().getAttribute("onlineUser");
            //获取载荷内容
            JwtUser jwtUser = JwtUtils.getParamByToken(token);

            String loginName = jwtUser.getAccount();
            if (StringUtils.isBlank(loginName) && onlineUser != null){
                loginName = onlineUser.getLoginName();
            }
            String username = jwtUser.getname();
            if (StringUtils.isBlank(username) && onlineUser != null){
                username = onlineUser.getUsername();
            }
            return username + "（"+loginName+"）";
        }
        return "";
    }

    /*@Around("@annotation(com.fxsen.common.core.annotation.RecordLog)")
    public Object process(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) (pjp.getSignature());
        Class<?> currentClass = pjp.getTarget().getClass();
        String className = currentClass.getSimpleName();
        String methodName = currentClass.getMethod(signature.getName(), signature.getParameterTypes()).getName();
        logger.info("======= 开始执行：" + className + " — " + methodName + " ========");
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest req = attributes.getRequest();
        // 从请求头中取出 token  这里需要和前端约定好把jwt放到请求头一个叫Authorization的地方
        String token = req.getHeader(JwtUtils.JWT_TOKEN_HEADER);
        String operationUser = "";
        if (StringUtils.isNotBlank(token)){
            OnlineUser onlineUser = (OnlineUser)req.getSession().getAttribute("onlineUser");
            //获取载荷内容
            String loginName = JwtUtils.getClaimByName(token, "loginName").asString();
            if (StringUtils.isBlank(loginName) && onlineUser != null){
                loginName = onlineUser.getLoginName();
            }
            String username = JwtUtils.getClaimByName(token, "username").asString();
            if (StringUtils.isBlank(username) && onlineUser != null){
                username = onlineUser.getUsername();
            }
            operationUser = username + "（"+loginName+"）";
        }
        String ipAddress = HttpContextUtils.getIpAddress(req);
        //获取到请求参数
        Object[] pjpArgs = pjp.getArgs();
        //获取到方法上RecordLog注解
        Method method = currentClass.getMethod(signature.getName(), signature.getParameterTypes());
        RecordLog recordLog = method.getAnnotation(RecordLog.class);
        String value = recordLog.value();
        OperationLog operationLog = new OperationLog();
        operationLog.setOperationUser(operationUser);
        operationLog.setType(value.equals("登录") || value.equals("登出") ? 0 : 1);
        operationLog.setName(recordLog.value());
        operationLog.setRequestUrl(req.getRequestURL().toString());
        operationLog.setRequestMethod(req.getMethod());
        operationLog.setMethodName(signature.getDeclaringTypeName() + "." + signature.getName());
        operationLog.setParam(JSON.toJSONString(pjpArgs[0]));
        operationLog.setRequestIp(ipAddress);
        operationLog.setRealAddress(HttpContextUtils.getIpAddr(ipAddress));
        operationLogService.save(operationLog);
        Object obj = pjp.proceed();
        logger.info("======= 执行结束：" + className + " — " + methodName + " ========");
        return obj;
    }*/
}

